/* Compile options:  -ml (Large code model) */
#include "maindefs.h"
#include <stdio.h>
#include <usart.h>
#include <i2c.h>
#include <timers.h>
#include <delays.h>
#include "interrupts.h"
#include "messages.h"
#include "my_uart.h"
#include "my_i2c.h"
#include "uart_thread.h"
#include "timer1_thread.h"
#include "timer0_thread.h"
#include "adc_thread.h"
#include "user_interrupts.h"

#if defined(__18F45J10)

#pragma config WDTEN = OFF
#pragma config FOSC = HSPLL

#elif defined(__18F2680)

#pragma config OSC = IRCIO67    // Oscillator Selection bits (11XX External RC oscillator, CLKO function on RA6)
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))

#endif

#if SENSORS_PIC

#define SLAVE_ADDR 0x4F         // this is the 7-bit adress put in SSPADD<7:1>

#elif LOCONET_PIC

#define SLAVE_ADDR 0x4E         // this is the 7-bit adress put in SSPADD<7:1>

#endif





// This program 
//   (1) prints to the UART and it reads from the UART
//   (2) it "prints" what it reads from the UART to portb (where LEDs are connected)
//   (3) it uses two timers to interrupt at different rates and drive 2 LEDs (on portb)

void main(void)
{
	char c;
	signed char length;
	signed char retval;
	unsigned char msgtype;
	unsigned char msgbuffer[MSGLEN + 1];
	unsigned char i;
	
#if I2C_ON
	i2c_comm ic;
#endif

#if UART_ON
	uart_comm uc;
	uart_thread_struct uthread_data; // info for uart_lthread
#endif

#if TMR1_ON
	timer1_thread_struct t1thread_data; // info for timer1_lthread
#endif

#if TMR0_ON
	timer0_thread_struct t0thread_data; // info for timer0_lthread
#endif

#if ADC_ON
	adc_thread_struct adcthread_data; // info for adc_lthread
#if SENSORS_PIC
	unsigned char sensor_data[8]; // just tracking the upper byte, needs to be
	unsigned char adc_chan_counter;
	// shifted left two once received
#endif
#endif

#if defined(__18F2680)
	// set to run really, really fast...
	OSCCON = 0x6C; // 4 MHz
#endif
	OSCTUNEbits.PLLEN = 1; // 4x the clock speed in the previous line

#if UART_ON
	// initialize my uart recv handling code
	init_uart_recv(&uc);
#if LOCONET_PIC
	// USART RX interrupt, 1 = high priority
	IPR1bits.RCIP = 1;
#else
	// USART RX interrupt, 0 = low priority
	IPR1bits.RCIP = 0;
#endif
#endif
	// configure the hardware USART device
#if defined(__18F2680)
#if LOCONET_PIC
	// changing the sprg to be 14 to achieve the desired baud rate of 16.66Kbaud @ 16 MHz (loconet)
	OpenUSART(USART_TX_INT_OFF & USART_RX_INT_ON & USART_ASYNCH_MODE & USART_EIGHT_BIT &
			USART_CONT_RX & USART_BRGH_LOW, 14);
#else
	// changing the sprg to be 0x19 to achieve the desired baud rate of 9600 @ 16 MHz
	OpenUSART(USART_TX_INT_OFF & USART_RX_INT_ON & USART_ASYNCH_MODE & USART_EIGHT_BIT &
			USART_CONT_RX & USART_BRGH_LOW, 0x19);
#endif
#elif defined(__18F45J10)
	// changing the sprg to be 0x4C to achieve the desired baud rate of 9600 @ 48 MHz
	OpenUSART(USART_TX_INT_OFF & USART_RX_INT_ON & USART_ASYNCH_MODE & USART_EIGHT_BIT &
			USART_CONT_RX & USART_BRGH_LOW, 0x4C);
#endif

#if I2C_ON
	// initialize the i2c code
	init_i2c(&ic);

	// I2C interrupt priority, 1 = high
	IPR1bits.SSPIP = 1;

	// configure the hardware i2c device as a slave
	// need to shift the actual address left one bit, the LSB has to be 0
	i2c_configure_slave(SLAVE_ADDR + SLAVE_ADDR);

	// must specifically enable the I2C interrupts
	PIE1bits.SSPIE = 1;
	//I2CLED = 0;
#endif

#if TMR1_ON
	// init the timer1 lthread
	init_timer1_lthread(&t1thread_data);

	// initialize timer 1
	OpenTimer1( TIMER_INT_OFF & T1_PS_1_1 & T1_8BIT_RW & T1_SOURCE_INT & T1_OSC1EN_OFF & T1_SYNC_EXT_OFF);

	// Timer1 interrupt priority, 0 = low
	IPR1bits.TMR1IP = 0;
#endif

#if TMR0_ON
	//	 initialize timer 0
	OpenTimer0(TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_1);
	//	OpenTimer0( TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_256);
#endif

#if ADC_ON
	// init the adc lthread
	init_adc_thread(&adcthread_data);

	// ADC interrupt priority, 1 = high
	IPR1bits.ADIP = 1;

	// setup the ADC
	ADCON1 = 0b00001110; //VSS,VDD ref. AN0 analog only
	ADCON0 = 0x00; //clear ADCON0 to select channel 0 (AN0)
	ADCON2 = 0b00010010; //ADCON2 setup: Left justified, Tacq=4Tad, Tad=32*Tosc (or Fosc/16)
	ADCON0bits.ADON = 0x01; //Enable A/D module

	// setup interrupts for ADC
	PIR1bits.ADIF = 0;
	PIE1bits.ADIE = 1;
	INTCONbits.GIE = 1;

	// Start the first conversion
	ADCON0bits.GO_DONE = 1;
#endif

	// initialize message queues before enabling any interrupts
	init_queues();

	// set direction for PORTB to output. port b output used for debugging
	TRISB = 0x0;
	LATB = 0x0;

#if SENSORS_PIC
	SENSOR0_PWR = 1;
	SENSOR1_PWR = 1;
	SENSOR2_PWR = 1;

	TRISAbits.TRISA0 = 1;
	TRISAbits.TRISA1 = 1;
	TRISAbits.TRISA2 = 1;
	TRISAbits.TRISA3 = 1;
#endif


#if LOCONET_PIC
	// Using PORTA on loconet pic for digiview debugging
//	TRISA = 0x0;
//	LATA = 0x0;
#endif

#if defined(__18F2680)
	// Initialize A0, A1, A2 LED

	// Set RC0, RC1, RC2 as outputs
	TRISCbits.RC0 = 0;
	TRISCbits.RC1 = 0;
	TRISCbits.RC2 = 0;

	// Set LEDs to be OFF
	LATCbits.LATC0 = 0;
	LATCbits.LATC1 = 0;
	LATCbits.LATC2 = 0;
#endif

	// Peripheral interrupts can have their priority set to high or low
	// enable high-priority interrupts and low-priority interrupts
	enable_interrupts();

	//printf("Hello\r\n");
	// loop forever
	// This loop is responsible for "handing off" messages to the subroutines
	// that should get them.  Although the subroutines are not threads, but
	// they can be equated with the tasks in your task diagram if you 
	// structure them properly.
	while (1) {
		// Call a routine that blocks until either on the incoming
		// messages queues has a message (this may put the processor into
		// an idle mode
		block_on_To_msgqueues();

		// At this point, one or both of the queues has a message.  It 
		// makes sense to check the high-priority messages first -- in fact,
		// you may only want to check the low-priority messages when there
		// is not a high priority message.  That is a design decision and
		// I haven't done it here. Currently checks both, high checked first.
		length = ToMainHigh_recvmsg(MSGLEN, &msgtype, (void *) msgbuffer);
		if (length < 0) {
			// no message, check the error code to see if it is concern
			if (length != MSGQUEUE_EMPTY) {
				//printf("Error: Bad high priority receive, code = %x\r\n",
//						length);
				HANDLE_ERROR(length);
			}
		} else {
			switch (msgtype) {
			case MSGT_TIMER0:
			{
#if TMR0_ON
				timer0_lthread(&t0thread_data, msgtype, length, msgbuffer);
#endif
				break;
			};
			case MSGT_I2C_DATA: // this type executes the MSGT_I2C_DATA case
			case MSGT_I2C_DBG:
			{
				//printf("I2C Interrupt received %x: ", msgtype);
				for (i = 0; i < length; i++) {
					//printf(" %x", msgbuffer[i]);
				}
				//printf("\r\n");
				// keep track of the first byte received for later use
				//last_reg_recvd = msgbuffer[0];
				// Send this value to the low priority message queue to be rcvd from i2c int handler
				// NOTE: not positive this will work
				//retval = FromMainLow_sendmsg(sizeof(unsigned char), MSGT_I2C_REG_RQST, (void *) msgbuffer);
				break;
			};
			case MSGT_ADC:
			{
#if ADC_ON
				adc_lthread(&adcthread_data, msgtype, length, msgbuffer);
#if SENSORS_PIC
				sensor_data[adc_chan_counter] = adcthread_data.adc_buf[0];
				adc_chan_counter++;
				if (adcthread_data.chan == 10) {
					// Once we have read all the sensors, send the data
					// Pass sensor data to i2c
					adc_chan_counter = 0;
					retval = FromMainHigh_sendmsg(8, MSGT_SENSOR_DATA, (void *) sensor_data);
					if (retval < 0) {
						HANDLE_ERROR(retval);
					}
					// WE do not start an A/D conversion here, it will be started once the 
					// i2c message has been sent
				}
				else {
					// Otherwise, start an A/D conversion to get the rest
					ADCON0bits.GO_DONE = 1;
				}
					
#endif
#endif
				break;
			};
			case MSGT_OVERRUN:
			case MSGT_UART_DATA:
			{
#if UART_ON
#if LOCONET_PIC
				retval = FromMainHigh_sendmsg(length, MSGT_LOCONET_MSG, (void *) msgbuffer);
				if(retval < 0) {
					HANDLE_ERROR(retval);
				}
//				LATAbits.LATA1 = 0;

#endif
#endif
				break;
			};
			case MSGT_I2C_RQST:
			default:
			{
				//printf("Error: Unexpected msg in queue, type = %x\r\n",
//						msgtype);
				HANDLE_ERROR(msgtype);
				break;
			};
			};
		}

		// Check the low priority queue
		length = ToMainLow_recvmsg(MSGLEN, &msgtype, (void *) msgbuffer);
		if (length < 0) {
			// no message, check the error code to see if it is concern
			if (length != MSGQUEUE_EMPTY) {
				//printf("Error: Bad low priority receive, code = %x\r\n",
//						length);
				HANDLE_ERROR(length);
			}
		} else {
			switch (msgtype) {
			case MSGT_TIMER1:
			{
#if TMR1_ON
				timer1_lthread(&t1thread_data, msgtype, length, msgbuffer);
#endif
				break;
			};
			case MSGT_OVERRUN:
			case MSGT_UART_DATA:
			{
#if UART_ON
				uart_lthread(&uthread_data, msgtype, length, msgbuffer);
#endif
				break;
			};
			default:
			{
				//printf("Error: Unexpected msg in queue, type = %x\r\n",
				//		msgtype);
				HANDLE_ERROR(msgtype);
				break;
			};
			};
		}
	}

}
